
import java.io.*;
import java.util.*;

public class RoleVisitor extends UnparseVisitor {

    private String fileName;
    VisitorHelper helper;

    public RoleVisitor(PrintStream o, String fileName) {
        super(o);
        this.fileName = fileName;
        helper = new VisitorHelper(fileName);

    }

    public RoleVisitor(PrintStream o, String fileName, VisitorHelper helper) {
        this(o, fileName);
        this.helper = (helper == null) ? new VisitorHelper(fileName) : helper;
    }
    //CS stands for CurrentScope, a CS Object would be better.
    
    
		
		private String currentScope = "";
		private String cSIdentifier = "";
		private boolean isCSDefinition = false;
		private boolean isCSRole = false;
		private boolean isCSInterface = false;
		private boolean isCSAbstract = false;
		private boolean isCSRelation = false;
		private boolean hasCSImplements = false;
		private boolean hasCSExtends = false;
		private String cSExtend = "";
		
		
		
		
	
    public static String oldId = "";
    

    /* Never print a token from here.
     * Always use super.print(token).
     * Without that, all special tokens before the token would be lost.
     */
    public Object visit(ASTPlayedbyList node, Object data) {
        if(helper.is(fileName, currentScope, node.player, helper.ROLE)) {
			throw new RuntimeException("A role cannot play a role");
		}
        return data;
    }

    public Object visit(ASTModifiers node, Object data) {

        /*This solves a javacc bug: empty node can generate errors while printing.*/
        String[] modifiers = {"abstract", "final", "native", "private", "protected", "public", "static", "strictfp",
            "synchronized", "transient", "volatile"};//beware of alphabetic order!
        //can have a child: annotation()
        if (java.util.Arrays.binarySearch(modifiers, node.jjtGetFirstToken().image) >= 0 || node.jjtGetNumChildren() > 0) {
			
            return super.visit(node, data);
        } else {
			
            return data;
        }
    }
    
    

    public Object visit(ASTThatBody node, Object data) {
        String id = cSIdentifier;

        // String thatType = roleTable.get(id).getValue();
        if(isCSDefinition){
			String thatType = helper.getPlayer(fileName, currentScope, id);



			String institution = oldId;

			out.println("");
			out.println("/**BEGIN** Added byJavaRoleParser:");
			out.println("***DO NOT EDIT THESE LINES*/");
			if (isCSDefinition && hasCSExtends) {
				out.println("    outer.super(that);");
			out.println("    this.outer=outer;");
				
			}
			out.println("	int alreadyPresent = 0;");
			out.println("	try");
			out.println("	{");
			out.println("			if (((it.unito.di.javarole.ObjectWithRoles)that).getRole(" + institution  +".this, \"" +((isCSDefinition && hasCSExtends) ? " outer, " : "") + id + "\") != null)");
			out.println("				alreadyPresent = 1;");
			out.println("	}");
			out.println("	catch (Exception e){}");
			out.println("	if (alreadyPresent == 1)");
			out.println("			this.that = (" + thatType + ")((it.unito.di.javarole.ObjectWithRoles)that).getRole(" + institution + ".this, \"" +((isCSDefinition && hasCSExtends) ? " outer, " : "") + id + "\");");
			out.println("	else");
			out.println("			this.that = that;");
			out.println(" 	((it.unito.di.javarole.ObjectWithRoles)this.that).setRole(this," +((isCSDefinition && hasCSExtends) ? " outer, " : "") + institution + ".this);");
			out.println("/** ");
			out.print("Addition by JavaRoleParser: **END**/");
			
		}
		
		return data;
    }

    public Object visit(ASTCDIRRBodyDeclaration node, Object data) {
        //
        boolean hasNoParConstructor = false;// node.hasNoParConstructor();	  
	
		//Am i the first child?
		if (node == node.jjtGetParent().jjtGetChild(0)) {
			SimpleNode parent = (SimpleNode) node.jjtGetParent();

			String spaces = "";
			for (int i = 1; i < node.jjtGetFirstToken().beginColumn; ++i) {
				spaces += " ";
			}
			
			if (isCSDefinition) {
				//Get the className, then that type with rolelist
				String identifier = cSIdentifier;
				//String thatType = roleTable.get(identifier).getValue();
				String thatType = helper.getPlayer(fileName, currentScope, identifier);
				String institution = oldId;
				if(isCSAbstract && isCSDefinition) institution+="Tuple";
				out.println(" ");
				out.println("/**BEGIN** Added byJavaRoleParser:");
				out.println("***DO NOT EDIT THESE LINES*/");
				out.println(spaces + "private " + thatType + " that;");
				
				if (hasCSExtends) {
					String relation = cSExtend.substring(0, cSExtend.lastIndexOf('.'));
					out.println(spaces + "private " + relation + "Tuple outer;");
				}
				//Adding necessary methods.
				out.println(spaces);

				out.println(spaces + " public void destroy() {");//attenzione la destroy dei ruoli in una relazione deve essere diversa f.f
				out.println(spaces + " 	((it.unito.di.javarole.ObjectWithRoles)this.that).removeRole(this, " + institution + ".this);");

				out.println(spaces + " 	this.that = null;");
				out.println(spaces + "}");
				out.println(spaces + " ");
				out.println(spaces + " public void transfer(Object req) {");
				out.println(spaces + " 	((it.unito.di.javarole.ObjectWithRoles)this.that).removeRole(this, " + institution + ".this);");
				out.println(spaces + " 	this.that = (" + thatType + ")req;");
				out.println(spaces + "	((it.unito.di.javarole.ObjectWithRoles)this.that).setRole(this," +((isCSDefinition && hasCSExtends) ? " outer, " : "") + institution + ".this);");
				out.println(spaces + "}");
				out.println(spaces + " ");
				out.println(spaces +"public "+thatType +" getThat(){");
				out.println(spaces +"	return that;");
				out.println(spaces +"}");
			}//end if(isDefinition)
			// Here, we add what is necessary to implement ObjectWithRoles methods

			if (!(isCSRole || isCSInterface || isCSRelation)) {
				if (!isCSDefinition) {

					out.println(" ");
					out.println("/**BEGIN** Added byJavaRoleParser:"); //to not write it twice
					out.println("***DO NOT EDIT THESE LINES*/");
				}

				out.println(spaces + "private java.util.Hashtable rolelist = new java.util.Hashtable();");
				out.println(spaces);
				
				out.println(spaces + "public void setRole(Object role, Object inst) {");
				
				out.println(spaces+"	if(rolelist.containsKey(inst.hashCode() + role.getClass().getSimpleName())) throw new RuntimeException(\"Role already defined\");");
				out.println(spaces + "	rolelist.put(inst.hashCode() + role.getClass().getSimpleName(),role);");
				out.println(spaces + "}");
				
				out.println(spaces+"public void setRole(Object role, Object outer, Object inst){");
				out.println(spaces+"	rolelist.put(inst.hashCode()+role.getClass().getSimpleName()+outer.hashCode(),role);");
				out.println(spaces+"}");
				
				
				out.println(spaces);
				out.println(spaces + "public Object getRole(Object inst, String role){");
				out.println(spaces + "		if ((rolelist.get(inst.hashCode() + role)) == null) throw new NullPointerException(\"NullRoleException\");");
				out.println(spaces + " 	return rolelist.get(inst.hashCode() + role);");
				out.println(spaces + "}");
				out.println(spaces + "public Object getRole(Object inst, Object rel, String role){");
				out.println(spaces + "		if ((rolelist.get(inst.hashCode() + rel.hashCode()+ role)) == null) throw new NullPointerException(\"NullRoleException\");");
				out.println(spaces + " 	return rolelist.get(inst.hashCode() + rel.hashCode()+role);");
				out.println(spaces + "}");
				out.println(spaces);
				out.println(spaces + "public void removeRole(Object role, Object inst) {");
				out.println(spaces + " 	rolelist.remove(inst.hashCode() + role.getClass().getSimpleName());");
				out.println(spaces + "}");
				out.println("/**");
				out.print("Addition by JavaRoleParser: **END**/");
			}

		

		}//end am i the first child		
		
		if(node.getTupleType()==2) return data;
		
        
        return super.visit(node, null);
    }

    public Object visit(ASTDefineConstructor node, Object data) {
        String identifier = cSIdentifier;
        //String thatType = roleTable.get(identifier).getValue();
        String thatType = helper.getPlayer(fileName, currentScope, identifier);
        String institution = oldId;
        if(isCSAbstract && isCSDefinition) institution+="Tuple";
        String relation = (isCSDefinition && hasCSExtends) ? (", " + cSExtend.substring(0, cSExtend.lastIndexOf('.')) + "Tuple outer") : "";
        out.println();
        out.println("/**BEGIN** Added byJavaRoleParser:");
        out.println("***DO NOT EDIT THESE LINES*/");

        out.println("public " + identifier + "Power" + "(" + thatType + " that " + relation + ") {");
        if (isCSDefinition && hasCSExtends) {
            out.println("    outer.super(that);");
            out.println("    this.outer=outer;");
        }
        out.println("    int alreadyPresent = 0;");
        out.println("    try");
        out.println("    {");
        out.println("    if (((it.unito.di.javarole.ObjectWithRoles)that).getRole(" + institution + ".this, \"" +((isCSDefinition && hasCSExtends) ? " outer, " : "") + identifier + "\") != null)");
        out.println("        alreadyPresent = 1;");
        out.println("    }");
        out.println("    catch (Exception e){}");
        out.println("    if (alreadyPresent == 1)");
        out.println("        this.that = (" + thatType + ")((it.unito.di.javarole.ObjectWithRoles)that).getRole(" + institution + ".this, \"" +((isCSDefinition && hasCSExtends) ? " outer, " : "") + identifier + "\");");
        out.println("    else");
        out.println("        this.that = that;");
        out.println("    ((it.unito.di.javarole.ObjectWithRoles)this.that).setRole(this," +((isCSDefinition && hasCSExtends) ? " outer, " : "") + institution + ".this);");
        out.println("}");
        out.println("/**");
        out.print("Addition by JavaRoleParser: **END**/");
        return data;

    }

    public Object visit(ASTClassOrInterfaceType node, Object data) {
        SimpleNode parent = (SimpleNode) node.parent;
        if (parent instanceof ASTAllocationExpression) {//to manage Role creation
            if (((ASTAllocationExpression) parent).getIdentifier() != null && helper.is(fileName, currentScope, ((ASTAllocationExpression) parent).getIdentifier(), helper.ROLE)) {
                node.jjtGetLastToken().image += "Power";
            }
            return super.visit(node, data);
        }

        if (parent instanceof ASTExtendsList) {
			/*definerole can extends abstract defineroles 
			 * but: 
			 * 
			 * a extends relation.a
			 * is a extends relationTuple.aPower
			 * */
			
            if (isCSDefinition) {
                node.jjtGetLastToken().image += "Power";
                
                
            } else if (isCSRole) {
                
                if (!helper.is(fileName, currentScope, node.jjtGetLastToken().image, helper.ROLE)) {
                    throw new RuntimeException("A role must extends a role");
                }
            }
            return super.visit(node, data);
        }

        SimpleNode gGGParent = parent != null ? (SimpleNode) parent.parent : null;
        gGGParent = gGGParent != null ? (SimpleNode) gGGParent.parent : null;


        if (gGGParent instanceof ASTCastExpression) {
            /*A role cast has the following grammar
             * 
             * (ClassName.)* varName.RoleName
             * 
             * I look for a non-ClassName token
             * 
             * if I find it it's a role cast.
             */
			
            String[] cast = roleCasting(((ASTCastExpression)gGGParent).getTotal());
            if (cast != null) {
                out.print(cast[1]);

                return data;
            } else {
                return super.visit(node, data);
            }
        }
        return super.visit(node, data);

    }

    public Object visit(ASTThatParameter node, Object data) {
			   
		String thatType = helper.getPlayer(fileName, currentScope, cSIdentifier);
		out.print(thatType + " that");
		if (hasCSExtends) {
			//grammar checks that . exists.
			String relation = cSExtend.substring(0, cSExtend.lastIndexOf('.'));
			out.print(", " + relation + "Tuple outer");
		}

	

        if (node.jjtGetParent().jjtGetNumChildren() > 1) {
            out.print(", ");//one is me
        }
        return data;
    }
    


    public Object visit(ASTCDIRRDeclaration node, Object data) {
        //save
        String[] strings = {cSExtend, cSIdentifier, currentScope, oldId};
        Boolean[] booleans = {hasCSExtends, hasCSImplements, isCSAbstract, isCSDefinition, isCSInterface, isCSRelation, isCSRole};
		
        newState(node);
        Token type = node.jjtGetFirstToken();//class, definerole..		
        if (node.isRole()) {
            type.image = "interface";
        }
        if (node.isDefinition() || node.isRelation()) {
            if(data instanceof String) out.print("abstract");
            type.image = "class";
        }

        String spaces = "    ";
        for (int i = 0; i < type.beginColumn; i++) {
            spaces += " ";
        }

        super.print(type);
        super.print(type.next);//name
        
        //Qui potrei spostare di padre il this se è abstract.. ma come controllo di non farlo all'infinito? 
        if (node.isDefinition) {
            out.print("Power");
            ASTCDIRRDeclaration ancestor = node.getRelationAncestor();//return null if none
            if (ancestor != null) {
                if (!node.isAbstract) {
                    throw new RuntimeException("You can define just abstract roles within a relationship at line " + node.jjtGetFirstToken().beginLine);
                } else {
                    ancestor.addNumOfDefine();
                }
            }


        }
        if(node.isRelation){
			out.print("Tuple");
		}
 
        int sibling = 0;
        SimpleNode child = (SimpleNode) node.jjtGetChild(sibling);

        if (!(node.isRole() || node.isRelation())) {
            if (node.hasExtends() && node.isDefinition()) {
                try {
                    //if (!abstractsList.contains(new Info(node.getExtend()))) {
                    if (!helper.is(fileName, currentScope, node.getExtend(), helper.ABSTRACTDEFINE)) {
                        throw new ParseException("Puoi estendere solo ruoli astratti");
                    }
                } catch (Exception x) {
                    System.err.println("Ruolo " + type.next.image + " non estende nessuno tra i ruoli astratti" +node.getExtend());

                }
            }
            if (child instanceof ASTTypeParameters) {
                super.visit((ASTTypeParameters) child, data);
                child = (SimpleNode) node.jjtGetChild(++sibling);
            }
            if (node.hasExtends()) {
                super.visit((ASTExtendsList) child, data);
                child = (SimpleNode) node.jjtGetChild(++sibling);
            }
            if (!node.isInterface()) {
                if (!(node.hasImplements())) {
                    out.print(" implements ");
                } else {
                    super.visit((ASTImplementsList) child, data);
                    child = (SimpleNode) node.jjtGetChild(++sibling);
                    out.print(", ");
                }
                out.print("it.unito.di.javarole.ObjectWithRoles");
                if (node.isDefinition()) {
                    out.print(", " + node.getIdentifier());//id è uguale all'interfaccia
                }
            }
            //super.visit((ASTCDIRRBody) child, data);
        }//end if(!(role | relation))
        else if (node.isRole()) {			//role can have 3 childs or 2. (first is playedbylist and last is body)

            visit((ASTPlayedbyList) child, data);
            child = (SimpleNode) node.jjtGetChild(++sibling);
            if (node.jjtGetNumChildren() > 2) {
                super.visit((ASTExtendsList) child, data);
                out.println(", it.unito.di.javarole.RoleInterface<"+helper.getPlayer(fileName, currentScope, cSIdentifier)+">");
            }
            else out.println(" extends it.unito.di.javarole.RoleInterface<"+helper.getPlayer(fileName, currentScope, cSIdentifier)+">");
        }
        
        visit((ASTCDIRRBody) child, data);
        if (node.isRelation && node.getNumOfDefine() != 2) {
            throw new RuntimeException("You must define exactly 2 roles in a relation (" + node.getIdentifier() + ") not " + node.getNumOfDefine());
        }
        oldState(strings, booleans);


        return data;

    }//end visit

	
	
	public Object visit(ASTTuple node, Object data){
		return null;
	}
	
	public Object visit (ASTCDIRRBody node , Object data){
		
		if(isCSRelation){
			
			ASTCheckMethod manager = (ASTCheckMethod)node.jjtGetChild(node.jjtGetNumChildren()-1);
			
			for(int i =0;i<node.jjtGetNumChildren();i++){
				SimpleNode child = (SimpleNode)node.jjtGetChild(i);
				if(child instanceof ASTCDIRRBodyDeclaration){
					if(((ASTCDIRRBodyDeclaration)child).getTupleType()==2){
						SimpleNode clone = child.powerClone();//clona soltanto l'indispensabile
						manager.jjtAddChild(child.powerClone(),manager.jjtGetNumChildren());	
						clone.jjtSetParent(manager);
						child.notPrintable();
									//la print di checkmethod è fatta in modo che lastToken.next dei figli sia inifluente
					}
				}
			}
			//ricompatto il node
			
						
				
				
			
		}
					
		return super.visit(node,data);
	}

	public Object visit(ASTCheckMethod node, Object data){
		out.println("/**BEGIN** Added byJavaRoleParser:");
		out.println("***DO NOT EDIT THESE LINES*/");
		out.println("}");//chiudo la relationship
		ASTCDIRRDeclaration relationDeclaration = (ASTCDIRRDeclaration) ((SimpleNode) node.jjtGetParent()).jjtGetParent();
		String id = relationDeclaration.getIdentifier();
		out.println("class "+id+"{");
        String from= relationDeclaration.getFrom();
        String to=relationDeclaration.getTo();
        String fromPlayer = helper.getPlayer(fileName, currentScope, from);
        String toPlayer = helper.getPlayer(fileName, currentScope, to);
        
        
        out.println("java.util.Vector<"+id+"Tuple> tuples;");
        /*Costruttore*/
        out.println("public "+id+"(){");
        out.println("	tuples=new java.util.Vector<"+id+"Tuple>();");
        out.println("}");
		/*Costruttore-fine*/
		
		/*Add*/
		out.println("public void add("+fromPlayer+" $from, "+toPlayer+" $to){");
		out.println("	for("+id+"Tuple t : tuples){");
		out.println("		if(t.getFromPlayer().equals($from) && t.getToPlayer().equals($to)) return;");
		out.println("	}");
		out.println("	tuples.add(new "+id+"Tuple($from, $to));");
		out.println("}");
		
		out.println("public void add("+fromPlayer+"[] $from, "+toPlayer+" $to){");
		out.println("	for("+fromPlayer+" f : $from){");
		out.println("		boolean exists=false;");
		out.println("		for("+id+"Tuple t : tuples){");
		out.println("			if(t.getFromPlayer().equals(f) && t.getToPlayer().equals($to)) {;");
		out.println("				exists=true;");
		out.println("				break;");
		out.println("			}");	
		out.println("		}");
		out.println("		if(!exists) tuples.add(new "+id+"Tuple(f,$to));");
		out.println("	}");
		out.println("}");
		
		out.println("public void add("+fromPlayer+" $from, "+toPlayer+"[] $to){");
		out.println("	for("+toPlayer+" t : $to){");
		out.println("		boolean exists=false;");
		out.println("		for("+id+"Tuple tu : tuples){");
		out.println("			if(tu.getFromPlayer().equals($from) && tu.getToPlayer().equals(t)) {;");
		out.println("				exists=true;");
		out.println("				break;");
		out.println("			}");	
		out.println("		}");
		out.println("		if(!exists) tuples.add(new "+id+"Tuple($from,t));");
		out.println("	}");
		out.println("}");
		
		out.println("public void add("+fromPlayer+"[] $from, "+toPlayer+"[] $to){");
		out.println("	for("+fromPlayer+" f : $from){");
		out.println("		for("+toPlayer+" t: $to){");
		out.println("			boolean exists=false;");
		out.println("			for("+id+"Tuple tu : tuples){");
		out.println("				if(tu.getFromPlayer().equals(f) && tu.getToPlayer().equals(t)) {;");
		out.println("					exists=true;");
		out.println("					break;");
		out.println("				}");	
		out.println("			}");
		out.println("			if(!exists) tuples.add(new "+id+"Tuple(f,t));");
		out.println("		}");
		out.println("	}");
		out.println("}");
		
		out.println("public void add("+fromPlayer+"[] $from, "+toPlayer+"[] $to, java.util.Comparator c, int result){");
		out.println("	for("+fromPlayer+" f : $from){");
		out.println("		for("+toPlayer+" t: $to){");
		out.println("			boolean exists=false;");
		out.println("			for("+id+"Tuple tu : tuples){");
		out.println("				if(tu.getFromPlayer().equals(f) && tu.getToPlayer().equals(t)) {;");
		out.println("					exists=true;");
		out.println("					break;");
		out.println("				}");	
		out.println("			}");
		out.println("			if(!exists) {");
		out.println("				if(c.compare(f,t)==result) tuples.add(new "+id+"Tuple(f,t));");
		out.println("			}");
		out.println("		}");
		out.println("	}");
		out.println("}");
		
		
		/*Add-fine*/
		
		/*If FromPlayer==ToPlayer??*/
		/*Get*/
		out.println("public "+from+"[] getFromSet("+toPlayer+" $to){");
		out.println("	java.util.HashSet<"+from+"> froms = new java.util.HashSet<"+from+">();");
		out.println("	for("+id+"Tuple t : tuples){");
		out.println("		if(t.getToPlayer().equals($to)) froms.add(t.getFrom());");
		out.println("	}");
		out.println("return ("+from+"[]) froms.toArray(new "+from+"[froms.size()]);");
		out.println("}");
		
		out.println("public "+to+"[] getToSet("+fromPlayer+" $from){");
		out.println("	java.util.HashSet<"+to+"> tos = new java.util.HashSet<"+to+">();");
		out.println("	for("+id+"Tuple t : tuples){");
		out.println("		if(t.getFromPlayer().equals($from)) tos.add(t.getTo());");
		out.println("	}");
		out.println("return ("+to+"[]) tos.toArray(new "+to+"[tos.size()]);");
		out.println("}");
		
		out.println("public "+from+"[] getFromSet("+toPlayer+"[] $to){");
		out.println("	java.util.HashSet<"+from+"> froms = new java.util.HashSet<"+from+">();");
		out.println("	for("+id+"Tuple t : tuples){");
		out.println("		for("+toPlayer+" to : $to){");
		out.println("			if(t.getToPlayer().equals(to)) froms.add(t.getFrom());");
		out.println("		}");
		out.println("	}");
		out.println("return ("+from+"[]) froms.toArray(new "+from+"[froms.size()]);");
		out.println("}");
		
		out.println("public "+from+"[] getFromSet("+toPlayer+"[] $to, java.util.Comparator c, int result){");
		out.println("	java.util.HashSet<"+from+"> froms = new java.util.HashSet<"+from+">();");
		out.println("	for("+id+"Tuple t : tuples){");
		out.println("		for("+toPlayer+" to : $to){");
		out.println("			if(t.getToPlayer().equals(to) && c.compare(t.getFromPlayer(),to)==result) froms.add(t.getFrom());");
		out.println("		}");
		out.println("	}");
		out.println("return ("+from+"[]) froms.toArray(new "+from+"[froms.size()]);");
		out.println("}");
		
		out.println("public "+to+"[] getToSet("+fromPlayer+"[] $from){");
		out.println("	java.util.HashSet<"+to+"> tos = new java.util.HashSet<"+to+">();");
		out.println("	for("+id+"Tuple t : tuples){");
		out.println("		for("+fromPlayer+" from: $from){");
		out.println("			if(t.getFromPlayer().equals(from)) tos.add(t.getTo());");
		out.println("		}");
		out.println("	}");
		out.println("return ("+to+"[]) tos.toArray(new "+to+"[tos.size()]);");
		out.println("}");
		
		out.println("public "+to+"[] getToSet("+fromPlayer+"[] $from, java.util.Comparator c, int result){");
		out.println("	java.util.HashSet<"+to+"> tos = new java.util.HashSet<"+to+">();");
		out.println("	for("+id+"Tuple t : tuples){");
		out.println("		for("+fromPlayer+" from: $from){");
		out.println("			if(t.getFromPlayer().equals(from) && c.compare(t.getFromPlayer(),from)==result) tos.add(t.getTo());");
		out.println("		}");
		out.println("	}");
		out.println("return ("+to+"[]) tos.toArray(new "+to+"[tos.size()]);");
		out.println("}");
		
		out.println("public "+from+" getFrom("+fromPlayer+" $from, "+toPlayer+" $to){");
		out.println("	for("+id+"Tuple t : tuples){");
		out.println("		if(t.getFromPlayer().equals($from) && t.getToPlayer().equals($to)){");
		out.println("			return t.getFrom();");
		out.println("		}");
		out.println("	}");
		out.println("return null;");
		out.println("}");
		
		out.println("public "+to+" getTo("+fromPlayer+" $from, "+toPlayer+" $to){");
		out.println("	for("+id+"Tuple t : tuples){");
		out.println("		if(t.getFromPlayer().equals($from) && t.getToPlayer().equals($to)){");
		out.println("			return t.getTo();");
		out.println("		}");
		out.println("	}");
		out.println("return null;");
		out.println("}");
		
		/*Get-fine*/
			
		/*Remove*/
		
		out.println("public int removeFrom("+fromPlayer+" $from){");
		out.println("	int counter =0;");
		out.println("	for("+id+"Tuple t : tuples){");
		out.println("		if(t.getFromPlayer().equals($from)){");
		out.println("			t.destroy();");
		out.println("			tuples.remove(t);");
		out.println("			counter++;");
		out.println("		}");
		out.println("	}");
		out.println("	return counter;");
		out.println("}");
		
		out.println("public int removeTo("+toPlayer+" $to){");
		out.println("	int counter =0;");
		out.println("	for("+id+"Tuple t : tuples){");
		out.println("		if(t.getToPlayer().equals($to)){");
		out.println("			t.destroy();");
		out.println("			tuples.remove(t);");
		out.println("			counter++;");
		out.println("		}");
		out.println("	}");
		out.println("	return counter;");
		out.println("}");
		
		out.println("public int remove("+fromPlayer+" $from, "+toPlayer+" $to){");
		out.println("	int counter =0;");
		out.println("	for("+id+"Tuple t : tuples){");
		out.println("		if(t.getToPlayer().equals($to) && t.getFromPlayer().equals($from)){");
		out.println("			t.destroy();");
		out.println("			tuples.remove(t);");
		out.println("			counter++;");
		out.println("		}");
		out.println("	}");
		out.println("	return counter;");
		out.println("}");		
		
		out.println("public int removeFrom("+fromPlayer+"[] $from){");
		out.println("	int counter =0;");
		out.println("	for("+id+"Tuple t : tuples){");
		out.println("		for("+fromPlayer+" f : $from){");
		out.println("			if(t.getFromPlayer().equals(f)){");
		out.println("				t.destroy();");
		out.println("				tuples.remove(t);");
		out.println("				counter++;");
		out.println("			}");
		out.println("		}");
		out.println("	}");
		out.println("	return counter;");
		out.println("}");
		
		out.println("public int removeTo("+toPlayer+"[] $to){");
		out.println("	int counter =0;");
		out.println("	for("+id+"Tuple t : tuples){");
		out.println("		for("+toPlayer+" f : $to){");
		out.println("			if(t.getToPlayer().equals(f)){");
		out.println("				t.destroy();");
		out.println("				tuples.remove(t);");
		out.println("				counter++;");
		out.println("			}");
		out.println("		}");
		out.println("	}");
		out.println("	return counter;");
		out.println("}");
		
		
		
		/*Remove-fine*/
		out.println("/*");
		out.print("Addition by JavaRoleParser: **END**/");
		//la classe non la chiudo perchè c'è la graffa della relationship
		
		return super.visit(node,data);
	}
	
	
    public Object visit(ASTRelationSelectSet node, Object data) {

        ASTCDIRRDeclaration relationDeclaration = (ASTCDIRRDeclaration) ((SimpleNode) node.jjtGetParent()).jjtGetParent();
        String from= relationDeclaration.getFrom();
        String to=relationDeclaration.getTo();
        String fromPlayer = helper.getPlayer(fileName, currentScope, from);
        String toPlayer = helper.getPlayer(fileName, currentScope, to);
        String id = relationDeclaration.getIdentifier();
        out.println("");
        out.println("/**BEGIN** Added byJavaRoleParser:");
        out.println("***DO NOT EDIT THESE LINES*/");

		out.println("public "+id+"Tuple 	("+fromPlayer+" $from, "+toPlayer+" $to){");
		out.println("from = $to.new "+from+"Power($from,this);");
		out.println("to = $from.new "+to+"Power($to,this);");
		out.println("}");
		
        out.println(from+"Power from;");
		out.println(to+"Power to;");
		out.println(id+"Tuple tuple="+id+"Tuple.this;");//final?
		
		/*i methodi "tuple"*/
		
		out.println("public "+fromPlayer+" getFromPlayer(){");
		out.println("	return from.getThat();");
		out.println("}");
		
		out.println("public "+toPlayer+" getToPlayer(){");
		out.println("	return to.getThat();");
		out.println("}");
		
		out.println("public "+to+" getTo(){");
		out.println("	return to;");
		out.println("}");
		
		out.println("public "+from+" getFrom(){");
		out.println("	return from;");
		out.println("}");
		
		out.println("public void destroy(){");
		out.println("	from.destroy();");
		out.println("	to.destroy();");
		out.println("}");
		
        out.println("/** ");
        out.print("Addition by JavaRoleParser: **END**/");

        return data;


    }
    
    
    
    
    public Object visit(ASTCastExpression node, Object data) {
        /*Is type child ClassOrInterfaceType? Just to understand if is a role cast*/
        //Type is child n° 0
        SimpleNode child=null; 
        
        boolean found=false;
        for(int i =0;i<node.jjtGetNumChildren() && !found;i++){
			child= (SimpleNode) node.jjtGetChild(i);
			if(child instanceof ASTType) found=true;
		}
        while (child != null && !(child instanceof ASTClassOrInterfaceType)) {
			
            child = child.jjtGetNumChildren() > 0 ? (SimpleNode) child.jjtGetChild(0) : null;
            
        }
        
        if (child == null) {
            return super.visit(node, data);
        }
        String[] casting = roleCasting(node.getTotal());
        
		if(casting!=null && (casting.length==1 || casting.length==3)){
			//rel casting
			
			out.print(casting[0]);
			
			for(int i =0;i<node.jjtGetNumChildren();i++){
				SimpleNode n = (SimpleNode)node.jjtGetChild(i);
				if((n instanceof ASTUnaryExpressionNotPlusMinus) || (n instanceof ASTUnaryExpression)){
					visit(n,data);
				}
				
			}
			if(casting.length==3) out.print(", "+casting[2]);
			out.println(")");//chiusura del metodo
			return data;
		}
		
        if (casting != null) {
			
            super.visit(node, data);
            out.print(".getRole(");
            out.print(casting[0] + ",\"" + casting[1] + "Power" + "\") ");
            
            return data;
        }
        return super.visit(node, data);

    }

    /**
     * @return	[0] institution
     * 		   	[1] interface
     *		
     * or
     * 			[0] complete getFrom  Method if relation
     * 
     * or 
     * 			[0] getTo method Method if relation. 
     * 			[1] null
     *			[2] toVar
     * 	
     * 		  
     * 		   null if is not a role
     **/
    private String[] roleCasting(String id) {
        
        String[] classPaths = id.split("\\.");
        
        String fromOrToVar="";
        int pathCounter = 1;
        String classPath = classPaths[0];
        String relVar=classPath;
        String institution = "";
        while (pathCounter < classPaths.length && helper.is(fileName, currentScope, classPath, helper.CLASS)) {
			
			relVar = classPaths[pathCounter];
			
            classPath += "." + classPaths[pathCounter++];
            
        }
        
        if (pathCounter == classPaths.length) {
            return null;
        }
        //fino a qui era un cast a ruole Normale, ora controllo se è un cast a ruolo di una relazione
        boolean castRel = false;
        if(pathCounter < (classPaths.length-1) && !helper.is(fileName, currentScope, classPath, helper.CLASS)) {
			castRel = true;
			fromOrToVar = classPaths[pathCounter];
			
			
			pathCounter++;
		}
        if(castRel){
			String role = classPaths[pathCounter];
			
			String fromOrTo = helper.isFromOrTo(fileName, role);
			if(fromOrTo.compareTo("To")==0) return new String[] {relVar+".get"+fromOrTo+"("+fromOrToVar+", "};			
			return new String[] {relVar+".get"+fromOrTo+"(", null, fromOrToVar};
		}
        
        
        institution = classPaths[pathCounter - 1];
        String role = classPaths[pathCounter];
        for (int i = pathCounter + 1; i < classPaths.length; i++) {
            role += "." + classPaths[i];
        }
        if (helper.is(fileName, currentScope, role, helper.ROLE)) {
			if(helper.isFromOrTo(fileName,role)!=null){ throw new RuntimeException("You must specify the outer relationship to cast");}
            return new String[]{institution, role};
        } else {
            return null;
        }
    }

    
    private void newState(ASTCDIRRDeclaration node) {
        addScope(node.getIdentifier());
        oldId = cSIdentifier;
        cSIdentifier = node.getIdentifier();
        isCSDefinition = node.isDefinition();
        isCSRole = node.isRole();
        isCSInterface = node.isInterface();
        isCSAbstract = node.isAbstract();
        isCSRelation = node.isRelation();
        hasCSImplements = node.hasImplements();
        hasCSExtends = node.hasExtends();
        cSExtend = node.getExtend();
    }

    private void oldState(String[] strings, Boolean[] booleans) {
        //array are sorted by alphabets.        
        hasCSExtends = (boolean) booleans[0];
        hasCSImplements = (boolean) booleans[1];
        isCSAbstract = (boolean) booleans[2];
        isCSDefinition = (boolean) booleans[3];
        isCSInterface = (boolean) booleans[4];
        isCSRelation = (boolean) booleans[5];
        isCSRole = (boolean) booleans[6];
        cSExtend = strings[0];
        cSIdentifier = strings[1];
        oldId = strings[2];
        removeScope();
    }

    public void addScope(String scope) {
        if ("".compareTo(currentScope) != 0) {
            currentScope += ".";
        }
        currentScope += scope;
    }

    public void removeScope() {
        int indexOfDot = currentScope.lastIndexOf('.');
        if (indexOfDot != -1) {
            currentScope = currentScope.substring(0, indexOfDot);
        } else {
            currentScope = "";
        }
    }

    public VisitorHelper getHelper() {
        return helper;
    }
}

 
